package io.dcloud.sensor_module;

import android.annotation.SuppressLint;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;

import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;

import io.dcloud.application.DCLoudApplicationImpl;
import io.dcloud.feature.uniapp.AbsSDKInstance;
import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.bridge.UniJSCallback;
import io.dcloud.feature.uniapp.common.UniModule;
import io.dcloud.sensor_module.event.MyEvent;
import io.dcloud.sensor_module.event.MyEventManager;
import io.dcloud.sensor_module.event.MyListener;

public class SensorManager extends UniModule {

    private static SensorManager sm = new SensorManager();

    @UniJSMethod(uiThread = false)
    public static int testPlus(int i1, int i2) {
        return i1 + i2;
    }

    @UniJSMethod(uiThread = false)
    public int testPlus2(int i1, int i2) {
        return i1 + i2;
    }

    @UniJSMethod(uiThread = false)
    public String testPlus3() {
        return "123321";
    }

    @UniJSMethod(uiThread = false)
    public static String getContext() {
        try {
            return DCLoudApplicationImpl.self().getContext().getApplicationContext().getString(R.string.test_text);
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }


    private static UniJSCallback callback;

    private static BluetoothAdapter btAdp;

    private static Application app;

    public static void setContext(Application app) {
        SensorManager.app = app;
    }

//    private static int totalCount;

    @UniJSMethod(uiThread = true)
    @SuppressLint("MissingPermission")
    public static String searchBt(UniJSCallback callback) {
        logD("当前线程：" + Thread.currentThread().getName());
        try {
            Context context = app;
            if (app == null) {
                context = DCLoudApplicationImpl.self().getContext().getApplicationContext();
            }
            SensorManager.callback = callback;
            searchFinish = false;
            logD("1");
            if (btAdp == null) {
                logD("1.1");
                btAdp = ((BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
            }
            if (btAdp.isDiscovering()) {
                logD("2");
                btAdp.cancelDiscovery();
            }
            logD("3");
            IntentFilter filter = new IntentFilter();
            //开始查找
            filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
            //结束查找
            filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
            //查找设备
            filter.addAction(BluetoothDevice.ACTION_FOUND);
            //设备扫描可见改变 当我可以被看见时就会发送一个广播过来
            filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
            //绑定状态
            filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
            macList.clear();
            context.registerReceiver(br, filter);
            btAdp.startDiscovery();
            logD("4");
            return "正常直接结束";
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }

    private List<String> list;

    /**
     * 获取所有已绑定蓝牙连接
     * @param callback
     */
    @UniJSMethod(uiThread = true)
    @SuppressLint("MissingPermission")
    public void getBondedDevices(UniJSCallback callback) {
        list = new ArrayList<>();
        initBtAdap();
        Set<BluetoothDevice> pairedDevices = btAdp.getBondedDevices();
        if (pairedDevices.size() > 0) {
            for (BluetoothDevice device : pairedDevices) {
                if(device.getName().startsWith("AS1000")) {
                    String str = device.getAddress() + "--" + device.getName();
                    list.add(str);
                }
            }
        }
        callback.invoke(list);
    }


    /**
     * 连接设备
     * @param callback
     */
    @UniJSMethod(uiThread = true)
    @SuppressLint("MissingPermission")
    public void connectBt(String mac, UniJSCallback callback) {
        initBtAdap();
        try {
            connectingSocket = connect(btAdp.getRemoteDevice(mac));
            connectingSocket.connect();
            callback.invoke(true);
        }catch (Exception e){
            destroy();
            callback.invoke(false);
        }
    }

    private void initBtAdap(){
        Context context = app;
        if (app == null) {
            context = DCLoudApplicationImpl.self().getContext().getApplicationContext();
        }
        if (btAdp == null) {
            logD("1.1");
            btAdp = ((BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
        }
    }

    private static boolean searchFinish;


    private static BroadcastReceiver br = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            logD("4.1---" + action);
            if (action == null) {
                return;
            }
            BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//            SCAN_MODE_CHANGED
            switch (action) {
                case BluetoothDevice.ACTION_FOUND:
                    logD("5");
                    addDevice(btDevice);
                    break;
                case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
                    logD("6---" + macList);
                    searchFinish = true;
                    if (callback != null) {
                        callback.invoke(macList);
                        SensorManager.callback = null;
                        context.getApplicationContext().unregisterReceiver(br);
                    }
                    break;
            }
        }
    };

    @SuppressLint("MissingPermission")
    private static void addDevice(BluetoothDevice btDevice) {
        String mac = btDevice.getAddress();
        String name = btDevice.getName();
        logD("搜到设备：" + mac + "   " + name);
//        totalCount++;
        if (TextUtils.equals(name, "AS1000") && (!macList.contains(mac))) {
            macList.add(mac + "--" + name);
        }
    }

    private static final String TAG = "SensorManager";

    private static void logD(String log) {
        Log.d(TAG, log);
    }

    private static List<String> macList = new ArrayList<>();

    private static BluetoothSocket connectingSocket;

    private static AtomicBoolean isMeasure = new AtomicBoolean(false);

    private UniJSCallback resultCallback;

    @UniJSMethod(uiThread = false)
    @SuppressLint("MissingPermission")
    public static String setSocket(BluetoothSocket socket) {
        return socket.getRemoteDevice().getAddress();
    }

    /**
     * 向传感器请求测量数据，循环接受数据time秒后返回数据平均值
     *
     * @param timer 每次连接时间，以秒为单位
     * @param mac   蓝牙设备地址
     * @param type  要测量的数据类型，1为温度,2为转速，3为震动-加速度，4为震动-速度，5为震动-位移
     * @param sleep 创建与传感器连接后的缓冲时间，单位为秒，建议为3秒以上
     */
    @UniJSMethod(uiThread = false)
    @SuppressLint("MissingPermission")
    public static void measure(String mac, int type, int timer, int sleep, UniJSCallback resultCallback) {
        logD("测量方法调用----" + Thread.currentThread().getName());
        if (isMeasure.get()) {
            resultCallback.invoke(getResult(false, "数据读取已被占用"));
            return /*getResult(false, "数据读取已被占用")*/;
        }
        isMeasure.set(true);
        try {
            if (connectingSocket != null && (!TextUtils.equals(connectingSocket.getRemoteDevice().getAddress(), mac))) {
                logD("已有连接设备且连接设备不为本次要连接的设备");
                destroy();
            }

            if (connectingSocket == null) {
                logD("重新连接设备");
                if (btAdp == null) {
                    Context context = app;
                    if (app == null) {
                        context = DCLoudApplicationImpl.self().getContext();
                    }
                    logD("重新创建BluetoothAdapter");
                    btAdp = ((BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
                }
                btAdp.cancelDiscovery();
                connectingSocket = connect(btAdp.getRemoteDevice(mac));
                connectingSocket.connect();
                Thread.sleep(sleep * 1000);
            }
            long startTime = System.currentTimeMillis();
            byte[] writeData = getOutData(type);
            if (writeData == null) {
                resultCallback.invoke(getResult(false, "测量数据类型错误：" + type));
                return /*getResult(false, "测量数据类型错误：" + type)*/;
            }
//            Thread.sleep(2000);
            int count = 0;
            float total = 0f;
            Float max = null;
            String hex = "";
            String shex = "";
            while (true) {
                long connectTime = System.currentTimeMillis() - startTime;
                byte[] resultBytes = requestData(connectingSocket, writeData, type);
                logD("单次测量返回原始数据：" + Arrays.toString(resultBytes));
                hex = bytesToHex(resultBytes);
                shex = Arrays.toString(resultBytes);
                if (resultBytes != null) {
                    count++;
                    float resultFloat = parseResultValue(type, resultBytes);
                    logD("单次测量返回数值：" + resultFloat);
                    if (max == null) {
                        max = resultFloat;
                    } else {
                        max = (resultFloat > max) ? resultFloat : max;
                    }
                    total += resultFloat;
//                    callback.invokeAndKeepAlive(getResult(true, resultFloat));
                }
                if (connectTime >= (timer * 1000L)) {
                    break;
                }
            }
            if (count == 0) {
                resultCallback.invoke(getResult(false, "未成功获取传感器数据"));
//                return getResult(false, "未成功获取传感器数据");
            } else {
//                return getResult(true, total / count);//取平均值
//                return getResult(true, max);//取最大值
                float result = total / count;
                resultCallback.invoke(getResult(true, result, "获取结果结束,平均值：" + result + ",获取次数：" + count + ",hex:"+hex+",shex:"+shex));
            }
        } catch (Exception e) {
            e.printStackTrace();
            resultCallback.invoke(getResult(false, "获取数据报错：" + e));
        } finally {
            isMeasure.set(false);
        }
    }

    /**
     * 字节数组转16进制
     * @param bytes 需要转换的byte数组
     * @return  转换后的Hex字符串
     */
    public static String bytesToHex(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if(hex.length() < 2){
                sb.append(0);
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    private static float parseResultValue(int type, byte[] resultBytes) {
        switch (type) {
            case 1:
                return ResultUtils.parseTemperature(resultBytes);
            case 2:
                return ResultUtils.parseSpeed(resultBytes);
            case 3:
            case 4:
            case 5:
                return ResultUtils.parseVibration(resultBytes, type);
        }
        throw new RuntimeException("数据类型错误");
    }


    private static byte[] requestData(BluetoothSocket connectingSocket, byte[] writeData, int type) throws IOException {
        InputStream is = connectingSocket.getInputStream();
        int oldLength = is.available();
        if (oldLength != 0) {
            logD("读取垃圾数据---" + oldLength);
            is.read(new byte[oldLength]);
        }
        logD("请求数据：" + type + "----" + Arrays.toString(writeData));
        connectingSocket.getOutputStream().write(writeData);
        byte[] readData = null;
        while (true) {
            int length = is.available();
            if (length == getResultLengthByType(type)) {
                readData = new byte[length];
                is.read(readData);
                break;
            } else {
                SystemClock.sleep(200);
            }
        }
        return readData;
    }

    private static int getResultLengthByType(int type) {
        switch (type) {
            case 1:
            case 2:
                return 20;
            case 3:
            case 4:
            case 5:
                return 18;

        }
        return 0;
    }

    private static String getResult(boolean success, Object result, String info) {
        logD("生成返回结果：" + success + "---" + result);
        try {
            JSONObject jo = new JSONObject();
            jo.put("success", success);
            jo.put("result", result);
            if (!TextUtils.isEmpty(info)) {
                jo.put("info", info);
            }
            return jo.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static String getResult(boolean success, Object result) {
        String resultStr = getResult(success, result, null);
        MyEventManager.postMsg(resultStr,source);
        return resultStr;
    }

    private static byte[] getOutData(int type) {
        switch (type) {
            case 1:
                return TEMPERATURE;
            case 2:
                return ROTATING_SPEED;
            case 3:
                return VIBRATION_JSD;
            case 4:
                return VIBRATION_SD;
            case 5:
                return VIBRATION_WY;
        }
        return null;
    }


    @SuppressLint("MissingPermission")
    private static BluetoothSocket connect(BluetoothDevice device) {
        try {
            UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
            BluetoothSocket socket = device.createRfcommSocketToServiceRecord(uuid);
            return socket;
        } catch (IOException e) {
            e.printStackTrace();
            Method m = null;
            BluetoothSocket socket = null;
            try {
                m = device.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
                socket = (BluetoothSocket) m.invoke(device, 1);
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException noSuchMethodException) {
                noSuchMethodException.printStackTrace();
            }
            return socket;
        }
    }


    private static byte[] TEMPERATURE = {0x36, 0x14, (byte) 0xC5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x65, 0x42};//温度
    private static byte[] ROTATING_SPEED = {0x36, 0x14, (byte) 0xC3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xC7, (byte) 0xC1};//转速
    private static byte[] VIBRATION_JSD = {0x36, 0x14, (byte) 0xC1, 0x1, 0x07, 0xA, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5D, (byte) 0x8C};//加速度
    private static byte[] VIBRATION_SD = {0x36, 0x14, (byte) 0xC1, 0x1, 0x08, 0xA, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5D, (byte) 0x8C};//速度
    private static byte[] VIBRATION_WY = {0x36, 0x14, (byte) 0xC1, 0x1, 0x09, 0xA, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5D, (byte) 0x8C};//位移


    /**
     * 把byte数组转换成int类型
     * 源byte数组
     *
     * @return 返回的int值
     */
    public static int bytesToInt(byte... b) {
        int value = 0;
        switch (b.length) {
            case 1:
                value = ((b[0] & 0xFF) << 24);
                break;
            case 2:
                value = (((b[0] & 0xFF) << 24)
                        | ((b[1] & 0xFF) << 16));
                break;
            case 3:
                value = (((b[0] & 0xFF) << 24)
                        | ((b[1] & 0xFF) << 16)
                        | ((b[2] & 0xFF) << 8));
                break;
            case 4:
                value = (((b[0] & 0xFF) << 24)
                        | ((b[1] & 0xFF) << 16)
                        | ((b[2] & 0xFF) << 8)
                        | (b[3] & 0xFF));
                break;
        }
        return value;
    }


    @SuppressLint("MissingPermission")
    @UniJSMethod(uiThread = false)
    public static void destroy() {
        logD("断开连接调用");
        try {
            if (btAdp != null) {
                btAdp.cancelDiscovery();
                btAdp = null;
            }
            MyEventManager.getMyEventManager().removeAllListener();

            if (connectingSocket != null) {
                connectingSocket.close();
            }
            logD("已断开");
        } catch (Exception e) {
            e.printStackTrace();
            logD("断开报错：" + e.getMessage());
        } finally {
            connectingSocket = null;
        }
    }

    private static String source;

    @UniJSMethod(uiThread = false)
    public static void addListener(String source) {
        logD("断开连接调用");
        MyEventManager.getMyEventManager().addListener(new MyListener() {
            @Override
            public void onChange(MyEvent myEvent) {

                //从uniapp 或者原生传过来的数据
                Object data = myEvent.getData();

            }
        },source);
    }


}

class ResultUtils {
    //解析温度
    public static float parseTemperature(byte[] data) {
        int byteInt = bytesToInt(data[3], data[4], data[5], data[6]);
        int size = byteInt / 10;
        if (size > 200 || size < -10) {
            byteInt = byteArray2Int(data[3], data[4], data[5], data[6]);
        } else if (byteInt < -100) {
            byteInt = -100;
        }
        BigDecimal numsrc = new BigDecimal(byteInt);
        BigDecimal src100 = new BigDecimal("10");
        BigDecimal total = numsrc.divide(src100).setScale(1, BigDecimal.ROUND_HALF_UP);
        return total.floatValue();
    }

    //解析转速
    public static int parseSpeed(byte[] data) {
        int byteInt = byteArray2Int(data[5], data[6]);
        return byteInt;
    }

    /**
     * 解析震动，震动分为加速度，速度，位移三种，分别对应参数
     *
     * @param type 的 1 2 3
     *             每种震动计算结果又各自包含峰值，峰峰值，有效值三种，所以计算结果比较复杂，需要用实体类来返回
     */
    public static float parseVibration(byte[] data, int type) {
        VibrationResult result = new VibrationResult();
        result.resultArray = new float[3];
        DecimalFormat df = new DecimalFormat("#.00");
        byte up[] = {data[11], data[10], 0, 0};
        float record_up = byte2Float(up);//峰值
        result.resultArray[0] = Float.parseFloat(df.format(record_up));

        byte upup[] = {data[13], data[12], 0, 0};
        float record_upup = byte2Float(upup);//峰峰值
        result.resultArray[1] = Float.parseFloat(df.format(record_upup));

        byte Valid[] = {data[15], data[14], 0, 0};
        float record_Valid = byte2Float(Valid);//有效值
        result.resultArray[2] = Float.parseFloat(df.format(record_Valid));
        switch (type) {
            case 3://加速度
                return result.resultArray[0];
            case 4://速度
                return result.resultArray[2];
            case 5://位移
                return result.resultArray[1];
        }
        throw new RuntimeException("数据类型错误");
    }

    /**
     * 解析传感器返回的校正参数，
     *
     * @param data 为传感器返回的原始数据，
     * @params isTemp 为返回的是否为温度类型，true为温度类型，fale为其它类型
     */

    public static float[] parseCorrections(byte[] data, boolean isTemp) {
        float value1 = byte2Float(data[3], data[4], data[5], data[6]);
        if (isTemp) {
            return new float[]{value1};
        }
        float value2 = byte2Float(data[7], data[8], data[9], data[10]);
        float[] result = {
                value1, value2
        };
        return result;
    }

    public static byte[] float2byte(float f) {
        int fbit = Float.floatToIntBits(f);

        byte[] b = new byte[4];
        for (int i = 0; i < 4; i++) {
            b[i] = (byte) (fbit >> (24 - i * 8));
        }
        return b;
    }


    public static class VibrationResult {
        public String result;
        //长度为三，0为峰值，1为峰峰值，2为有效值
        public float[] resultArray;
    }

    /**
     * byte数组转换Float
     */
    public static float byte2Float(byte... data) {
        ByteBuffer buff = ByteBuffer.wrap(data);
        buff.order(ByteOrder.BIG_ENDIAN);
        return buff.getFloat();
    }


    /**
     * 把byte数组转换成int类型
     * 源byte数组
     *
     * @return 返回的int值
     */
    public static int bytesToInt(byte... b) {
        int value = 0;
        switch (b.length) {
            case 1:
                value = ((b[0] & 0xFF) << 24);
                break;
            case 2:
                value = (((b[0] & 0xFF) << 24)
                        | ((b[1] & 0xFF) << 16));
                break;
            case 3:
                value = (((b[0] & 0xFF) << 24)
                        | ((b[1] & 0xFF) << 16)
                        | ((b[2] & 0xFF) << 8));
                break;
            case 4:
                value = (((b[0] & 0xFF) << 24)
                        | ((b[1] & 0xFF) << 16)
                        | ((b[2] & 0xFF) << 8)
                        | (b[3] & 0xFF));
                break;
        }
        return value;
    }

    public static int byteArray2Int(byte... b) {
        int a = 0;
        switch (b.length) {
            case 1:
                a = b[0];
                if (a < 0) {
                    a = a + 256;
                }
                break;
            case 2:
                a = (((int) b[0]) << 8) + b[1];
                if (a < 0) {
                    a = a + 256;
                }
                break;
            case 3:
                a = (((int) b[0]) << 16) + (((int) b[1]) << 8) + b[2];
                if (a < 0) {
                    a = a + 256;
                }
                break;
            case 4:
                a = (((int) b[0]) << 24) + (((int) b[1]) << 16) + (((int) b[2]) << 8) + b[3];
                if (a < 0) {
                    a = a + 256;
                }
                break;
        }
        return a;
    }
}
